  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Prototype Unit test file</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <script src="../../dist/prototype.js" type="text/javascript"></script>
    <script src="../lib/unittest.js" type="text/javascript"></script>
    <link rel="stylesheet" href="../test.css" type="text/css" />
    <style type="text/css" media="screen">
    /* <![CDATA[ */
      #testlog { margin-bottom:2em }
    /* ]]> */
    </style>
  </head>
  <body>
  <h1>Prototype Unit test file</h1>
  <p>
    Test of utility functions in form.js
  </p>

  <!-- Log output -->
  <div id="testlog"> </div>

  <form id="form" method="get" action="fixtures/empty.js">
    <input type="text" name="val1" id="input_enabled" value="4" />
    <div>This is not a form element</div>
    <input type="text" name="val2" id="input_disabled" disabled="disabled" value="5" />
    <input type="submit" name="first_submit" value="Commit it!" />
    <input type="submit" name="second_submit" value="Delete it!" />
    <input type="text" name="action" value="blah" />
  </form>

  <form id="bigform" method="get" action="fixtures/empty.js">
    <div id="inputs">
      <input type="text"   name="dummy"  id="dummy_disabled" disabled="disabled"/>
      <input type="submit" name="commit" id="submit" />
      <input type="button" name="clicky" value="click me" />
      <input type="reset"  name="revert" />
      <input type="text"   name="greeting" id="focus_text" value="Hello" />
    </div>

    <!-- some edge cases in serialization -->
    <div id="value_checks">
      <input name="twin" type="text" value="" /> 
      <input name="twin" type="text" value="siamese" />
      <!-- Rails checkbox hack with hidden input: -->
      <input name="checky" type="checkbox" id="checkbox_hack" value="1" /> 
      <input name="checky" type="hidden" value="0" />
    </div>

    <!-- all variations of SELECT controls -->
    <div id="selects_wrapper">
      <select name="vu">
         <option value="1" selected="selected">One</option>
         <option value="2">Two</option>
         <option value="3">Three</option>
      </select>
      <select id="multiSel1" name="vm[]" multiple="multiple">
         <option id="multiSel1_opt1" value="1" selected="selected">One</option>
         <option id="multiSel1_opt2" value="2">Two</option>
         <option id="multiSel1_opt3" value="3" selected="selected">Three</option>
      </select>
      <select name="nvu">
         <option selected="selected">One</option>
         <option value="2">Two</option>
         <option value="3">Three</option>
      </select>

      <fieldset id="selects_fieldset">
        <select name="nvm[]" multiple="multiple">
           <option selected="selected">One</option>
           <option>Two</option>
           <option selected="selected">Three</option>
        </select>
        <select name="evu">
           <option value="" selected="selected">One</option>
           <option value="2">Two</option>
           <option value="3">Three</option>
        </select>
        <select name="evm[]" multiple="multiple">
           <option value="" selected="selected">One</option>
           <option>Two</option>
           <option selected="selected">Three</option>
        </select>
      </fieldset>
    </div>

    <div id="various">
      <select name="tf_selectOne"><option selected="selected"></option><option>1</option></select>
      <textarea name="tf_textarea"></textarea>
      <input type="checkbox" name="tf_checkbox" value="on" />
      <select name="tf_selectMany" multiple="multiple"></select>
      <input type="text" name="tf_text" />
      <div>This is not a form element</div>
      <input type="radio" name="tf_radio" value="on" />
      <input type="hidden" name="tf_hidden" />
      <input type="password" name="tf_password" />
    </div>
  </form>

  <form id="form_focus_hidden" style="display: none">
    <input type="text" />
  </form>

  <!-- tabindexed forms -->
  <div id="tabindex">
    <form id="ffe">
      <p><input type="text" disabled="disabled" id="ffe_disabled" /></p>
      <input type="hidden" id="ffe_hidden" />
      <input type="checkbox" id="ffe_checkbox" />
    </form>

    <form id="ffe_ti">
      <p><input type="text" disabled="disabled" id="ffe_ti_disabled" /></p>
      <input type="hidden" id="ffe_ti_hidden" />
      <input type="checkbox" id="ffe_ti_checkbox" />
      <input type="submit" id="ffe_ti_submit" tabindex="1" />
    </form>

    <form id="ffe_ti2">
      <p><input type="text" disabled="disabled" id="ffe_ti2_disabled" /></p>
      <input type="hidden" id="ffe_ti2_hidden" />
      <input type="checkbox" id="ffe_ti2_checkbox" tabindex="0" />
      <input type="submit" id="ffe_ti2_submit" tabindex="1" />
    </form>
  </div>

  <!-- Tests follow -->
  <script type="text/javascript" charset="utf-8">
  // <![CDATA[

    // sweet sweet additional assertions
    Object.extend(Test.Unit.Testcase.prototype, {
      assertEnabled: function() {
        for (var i = 0, element; element = arguments[i]; i++) {
          this.assert(!$(element).disabled, Test.Unit.inspect(element) + ' was disabled');
        }
      },
      assertDisabled: function() {
        for (var i = 0, element; element = arguments[i]; i++) {
          this.assert($(element).disabled, Test.Unit.inspect(element) + ' was enabled');
        }
      }
    });

  new Test.Unit.Runner({
    
    // Make sure to set defaults in the test forms, as some browsers override this
    // with previously entered values on page reload
    setup: function(){ with(this) {
      $$('form').each(function(f){ f.reset() });
      // hidden value does not reset (for some reason)
      $('bigform')['tf_hidden'].value = '';
    }},
    
    testDollarF: function(){ with(this) {
      assertEqual("4", $F("input_enabled"));
    }},
    
    testFormElementEventObserver: function(){ with(this) {
      var callbackCounter = 0;
      var observer = new Form.Element.EventObserver('input_enabled', function(){
        callbackCounter++;
      });
      
      assertEqual(0, callbackCounter);
      $('input_enabled').value = 'boo!';
      observer.onElementEvent(); // can't test the event directly, simulating
      assertEqual(1, callbackCounter);
    }},

    testFormElementObserver: function(){ with(this) {
      var timedCounter = 0;
      // First part: regular field
      var observer = new Form.Element.Observer('input_enabled', 0.5, function() {
        ++timedCounter;
      });

      // Test it's unchanged yet
      assertEqual(0, timedCounter);
      // Test it doesn't change on first check
      wait(550, function() {
        assertEqual(0, timedCounter);
        // Change, test it doesn't immediately change
        $('input_enabled').value = 'yowza!';
        assertEqual(0, timedCounter);
        // Test it changes on next check, but not again on the next
        wait(550, function() {
          assertEqual(1, timedCounter);
          wait(550, function() {
            assertEqual(1, timedCounter);
          });
        });
      });

      // Second part: multiple-select
      [1, 2, 3].each(function(index) {
        $('multiSel1_opt' + index).selected = (1 == index);
      });
      timedCounter = 0;
      observer = new Form.Element.Observer('multiSel1', 0.5, function() {
        ++timedCounter;
      });

      // Test it's unchanged yet
      assertEqual(0, timedCounter);
      // Test it doesn't change on first check
      wait(550, function() {
        assertEqual(0, timedCounter);
        // Change, test it doesn't immediately change
        // NOTE: it is important that the 3rd be re-selected, for the
        // serialize form to obtain the expected value :-)
        $('multiSel1_opt3').selected = true;
        assertEqual(0, timedCounter);
        // Test it changes on next check, but not again on the next
        wait(550, function() {
          assertEqual(1, timedCounter);
          wait(550, function() {
            assertEqual(1, timedCounter);
          });
        });
      });
    }},
    
    testFormObserver: function(){ with(this) {
      var timedCounter = 0;
      // should work the same way was Form.Element.Observer
      var observer = new Form.Observer('form', 0.5, function(form, value) {
        ++timedCounter;
      });

      // Test it's unchanged yet
      assertEqual(0, timedCounter);
      // Test it doesn't change on first check
      wait(550, function() {
        assertEqual(0, timedCounter);
        // Change, test it doesn't immediately change
        $('input_enabled').value = 'yowza!';
        assertEqual(0, timedCounter);
        // Test it changes on next check, but not again on the next
        wait(550, function() {
          assertEqual(1, timedCounter);
          wait(550, function() {
            assertEqual(1, timedCounter);
          });
        });
      });
    }},
    
    testFormEnabling: function(){ with(this) {
      var form = $('bigform')
      var input1 = $('dummy_disabled');
      var input2 = $('focus_text');
      
      assertDisabled(input1);
      assertEnabled(input2);
      
      form.disable();
      assertDisabled(input1, input2);
      form.enable();
      assertEnabled(input1, input2);
      input1.disable();
      assertDisabled(input1);
      
      // non-form elements:
      var fieldset = $('selects_fieldset');
      var fields = fieldset.immediateDescendants();
      fields.each(function(select) { assertEnabled(select) });
      
      Form.disable(fieldset)
      fields.each(function(select) { assertDisabled(select) });
      
      Form.enable(fieldset)
      fields.each(function(select) { assertEnabled(select) });
    }},
    
    testFormElementEnabling: function(){ with(this) {
      var field = $('input_disabled');
      field.enable();
      assertEnabled(field);
      field.disable();
      assertDisabled(field);
      
      var field = $('input_enabled');
      assertEnabled(field);
      field.disable();
      assertDisabled(field);
      field.enable();
      assertEnabled(field);
    }},

    // due to the lack of a DOM hasFocus() API method,
    // we're simulating things here a little bit
    testFormActivating: function(){ with(this) {
      // Firefox, IE, and Safari 2+
      function getSelection(element){
        try {
          if (typeof element.selectionStart == 'number') {
            return element.value.substring(element.selectionStart, element.selectionEnd);
          } else if (document.selection && document.selection.createRange) {
            return document.selection.createRange().text;
          }
        }
        catch(e){ return null }
      }
      
      // Form.focusFirstElement shouldn't focus disabled elements
      var element = Form.findFirstElement('bigform');
      assertEqual('submit', element.id);
      
      // Test IE doesn't select text on buttons
      Form.focusFirstElement('bigform');
      if(document.selection) assertEqual('', getSelection(element));
      
      // Form.Element.activate shouldn't select text on buttons
      element = $('focus_text');
      assertEqual('', getSelection(element));
        
      // Form.Element.activate should select text on text input elements
      element.activate();
      assertEqual('Hello', getSelection(element));

      // Form.Element.activate shouldn't raise an exception when the form or field is hidden
      assertNothingRaised(function() {
        $('form_focus_hidden').focusFirstElement();
      });
    }},
    
    testFormGetElements: function() {with(this) {
      var elements = Form.getElements('various'),
        names = $w('tf_selectOne tf_textarea tf_checkbox tf_selectMany tf_text tf_radio tf_hidden tf_password');
      assertEnumEqual(names, elements.pluck('name'))
    }},
    
    testFormGetInputs: function() {with(this){
      var form = $('form');
      [form.getInputs(), Form.getInputs(form)].each(function(inputs){
        assertEqual(inputs.length, 5);
        assert(inputs instanceof Array);
        assert(inputs.all(function(input) { return (input.tagName == "INPUT"); }));
      });
    }},

    testFormFindFirstElement: function() {with(this) {
      assertEqual($('ffe_checkbox'), $('ffe').findFirstElement());
      assertEqual($('ffe_ti_submit'), $('ffe_ti').findFirstElement());
      assertEqual($('ffe_ti2_checkbox'), $('ffe_ti2').findFirstElement());
    }},
    
    testFormSerialize: function() {with(this){
      // form is initially empty
      var form = $('bigform');
      var expected = { tf_selectOne:'', tf_textarea:'', tf_text:'', tf_hidden:'', tf_password:'' };
      assertHashEqual(expected, Form.serialize('various', true));
        
      // set up some stuff
      form['tf_selectOne'].selectedIndex = 1;
      form['tf_textarea'].value = "boo hoo!";
      form['tf_text'].value = "123öäü";
      form['tf_hidden'].value = "moo%hoo&test";
      form['tf_password'].value = 'sekrit code';
      form['tf_checkbox'].checked = true;
      form['tf_radio'].checked = true;
      var expected = { tf_selectOne:1, tf_textarea:"boo hoo!", tf_text:"123öäü",
        tf_hidden:"moo%hoo&test", tf_password:'sekrit code', tf_checkbox:'on', tf_radio:'on' }

      // return params
      assertHashEqual(expected, Form.serialize('various', true));
      // return string
      assertEnumEqual(Object.toQueryString(expected).split('&').sort(),
                      Form.serialize('various').split('&').sort());
      assertEqual('string', typeof $('form').serialize({ hash:false }));

      // Checks that disabled element is not included in serialized form.
      $('input_enabled').enable();
      assertHashEqual({ val1:4, action:'blah', first_submit:'Commit it!' },
                      $('form').serialize(true));

      // should not eat empty values for duplicate names 
      $('checkbox_hack').checked = false;
      var data = Form.serialize('value_checks', true); 
      assertEnumEqual(['', 'siamese'], data['twin']); 
      assertEqual('0', data['checky']);
      
      $('checkbox_hack').checked = true; 
      assertEnumEqual($w('1 0'), Form.serialize('value_checks', true)['checky']);

      // all kinds of SELECT controls
      var params = Form.serialize('selects_fieldset', true);
      var expected = { 'nvm[]':['One', 'Three'], evu:'', 'evm[]':['', 'Three'] };
      assertHashEqual(expected, params);
      params = Form.serialize('selects_wrapper', true);
      assertHashEqual(Object.extend(expected, { vu:1, 'vm[]':[1, 3], nvu:'One' }), params);

      // explicit submit button
      assertHashEqual({ val1:4, action:'blah', second_submit:'Delete it!' },
                      $('form').serialize({ submit: 'second_submit' }));
      assertHashEqual({ val1:4, action:'blah' },
                      $('form').serialize({ submit: false }));
      assertHashEqual({ val1:4, action:'blah' },
                      $('form').serialize({ submit: 'inexistent' }));
    }},
    
    testFormMethodsOnExtendedElements: function() {with(this) {
      var form = $('form');
      assertEqual(Form.serialize('form'), form.serialize());
      assertEqual(Form.Element.serialize('input_enabled'), $('input_enabled').serialize());
      assertNotEqual(form.serialize, $('input_enabled').serialize);
      
      Element.addMethods('INPUT',  { anInputMethod: function(input)  { return 'input'  } });
      Element.addMethods('SELECT', { aSelectMethod: function(select) { return 'select' } });

      form = $('bigform');
      var input = form['tf_text'], select = form['tf_selectOne'];
      input._extendedByPrototype = select._extendedByPrototype = false;

      assert($(input).anInputMethod);
      assert(!input.aSelectMethod);
      assertEqual('input', input.anInputMethod());

      assert($(select).aSelectMethod);
      assert(!select.anInputMethod);      
      assertEqual('select', select.aSelectMethod());
    }},
    
    testFormRequest: function() {with(this) {
      request = $("form").request();
      assert($("form").hasAttribute("method"));
      assert(request.url.include("fixtures/empty.js?val1=4"));
      assertEqual("get", request.method);
      
      request = $("form").request({ method: "put", parameters: {val2: "hello"} });
      assert(request.url.endsWith("fixtures/empty.js"));
      assertEqual(4, request.options.parameters['val1']);
      assertEqual('hello', request.options.parameters['val2']);
      assertEqual("post", request.method);
      assertEqual("put", request.parameters['_method']);

      // with empty action attribute
      request = $("ffe").request({ method: 'post' });
      assert(request.url.include("unit/form.html"),
        'wrong default action for form element with empty action attribute');
    }},
    
    testFormElementMethodsChaining: function(){ with(this) {
      var methods = $w('clear activate disable enable'),
        formElements = $('form').getElements();
      methods.each(function(method){
        formElements.each(function(element){
          var returned = element[method]();
          assertIdentical(element, returned);
        });
      });
    }},

    testSetValue: function(){ with(this) {
      // text input
      var input = $('input_enabled'), oldValue = input.getValue();
      assertEqual(input, input.setValue('foo'), 'setValue chaining is broken');
      assertEqual('foo', input.getValue(), 'value improperly set');
      input.setValue(oldValue);
      assertEqual(oldValue, input.getValue(), 'value improperly restored to original');

      // checkbox
      input = $('checkbox_hack');
      input.setValue(false);
      assertEqual(null, input.getValue(), 'checkbox should be unchecked');
      input.setValue(true);
      assertEqual("1", input.getValue(), 'checkbox should be checked');
      // selectbox
      input = $('bigform')['vu'];
      input.setValue('3');
      assertEqual('3', input.getValue(), 'single select option improperly set');
      input.setValue('1');
      assertEqual('1', input.getValue());
      // multiple select
      input = $('bigform')['vm[]'];
      input.setValue(['2', '3']);
      assertEnumEqual(['2', '3'], input.getValue(),
        'multiple select options improperly set');
      input.setValue(['1', '3']);
      assertEnumEqual(['1', '3'], input.getValue());
    }}
    
  });
// ]]>
</script>
</body>
</html>
